﻿//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Media;
using CommonBehaviors.Actions;
using MainDev.RemoteASM.Handlers;
using Styx;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals.World;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    public class TerraceOfEndlessSpring : Dungeon
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 526; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(958.4459, -51.12228, 513.1506); }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret.ToUnit();
                    if (unit != null)
                    {
                        if (unit.Entry == LeiShiId && unit.HasAura("Protect"))
                            return true;
                        if (unit.Entry == TerrorSpawnId && !Me.IsMelee())
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == CorruptedWatersId)
                        outgoingunits.Add(unit);

                    else if (unit.Entry == UnstableShaId && Me.IsDps())
                        outgoingunits.Add(unit);

                    else if (unit.Entry == TerrorSpawnId && unit.Distance < 80 && Me.IsMelee())
                        outgoingunits.Add(unit);

                    else if (_terraceGuardians.Contains(unit.Entry) && unit.Attackable && unit.Distance <= 40)
                        outgoingunits.Add(unit);
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == CorruptedWatersId && Me.IsDps())
                        priority.Score += 5000;

                    else if (unit.Entry == UnstableShaId && Me.IsDps())
                        priority.Score += (Me.IsRange() ? 5000 : -1000) - unit.Distance;

                    else if (unit.Entry == TerrorSpawnId && Me.IsMelee() && unit.Distance <= 100)
                        priority.Score = 5000 - unit.Distance;

                    else if (_terraceGuardians.Contains(unit.Entry) && unit.Attackable && unit.Distance <= 40)
                        priority.Score += 5000;
                }
            }
        }

        public override void OnEnter()
        {
            if (Me.IsTank())
            {
                Alert.Show(
                    "Tanking Not Supported",
                    string.Format(
                        "Tanking is not supported in the {0} script. If you wish to stay in raid and play manually then press 'Continue'. Otherwise you will automatically leave raid.",
                        Name),
                    30,
                    true,
                    true,
                    null,
                    () => Lua.DoString("LeaveParty()"),
                    "Continue",
                    "Leave");
            }
            else
            {
                Alert.Show(
                    "Do Not AFK",
                    "It is highly recommended you do not afk while in a raid and be prepared to intervene if needed in the event something goes wrong or you're asked to perform a certain task.",
                    20,
                    true,
                    false,
                    null,
                    null,
                    "Ok");
            }
        }

        #endregion

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(0)]
        public Composite RootEncounter()
        {
            return new PrioritySelector();
        }

        #region Protectors of the Endless

        private const uint CorruptedWatersId = 60621;
        private WoWPoint _protectersCenterLoc = new WoWPoint(-1017.489, -3048.979, 12.82353);

        // Lightning storm. - just heal through it. 
        [EncounterHandler(60583, "Protector Kaolan")]
        public Composite ProtectorKaolanEncounter()
        {
            WoWUnit boss = null;
            AddAvoidObject(ctx => boss != null && boss.IsValid && !boss.Combat && boss.IsAlive, 25, o => o == boss);
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }

        [EncounterHandler(60585, "Elder Regail")]
        public Composite ElderRegailEncounter()
        {
            WoWUnit boss = null;
            AddAvoidObject(ctx => boss != null && boss.IsValid && !boss.Combat && boss.IsAlive, 25, o => o == boss);
            AddAvoidObject(ctx => true, 7, o => o is WoWPlayer && (Me.HasAura("Lightning Prison") && !o.IsMe || !Me.HasAura("Lightning Prison") && o.ToUnit().HasAura("Lightning Prison")));

            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }

        [EncounterHandler(60586, "Elder Asani")]
        public Composite ElderAsaniEncounter()
        {
            WoWUnit boss = null;
            AddAvoidObject(ctx => boss != null && boss.IsValid && !boss.Combat && boss.IsAlive, 25, o => o == boss);
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }

        #endregion

        #region Tsulong

        private const uint SunbeamId = 62849;
        private const uint UnstableShaId = 62919;

        [EncounterHandler(62442, "Tsulong", Mode = CallBehaviorMode.Proximity)]
        public Composite TsulongEncounter()
        {
            const uint maxDreadShadowsStack = 8;
            const int nightMaresMissileId = 122770;
            WoWUnit boss = null;
            AddAvoidObject(ctx => boss != null && boss.IsValid && !boss.Combat && boss.HealthPercent == 100, 25, o => o == boss);
            WoWUnit sunbeam = null;
            WoWSpell dispellSpell = null;

            AddAvoidObject(ctx => !Me.HasAura("Dread Shadows") || Me.Auras["Dread Shadows"].StackCount < maxDreadShadowsStack, 7, SunbeamId);
            AddAvoidLocation(ctx => true, 9, o => ((WoWMissile)o).ImpactPosition, () => WoWMissile.InFlightMissiles.Where(m => m.SpellId == nightMaresMissileId));
            return new PrioritySelector(
                ctx =>
                {
                    sunbeam = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == SunbeamId);
                    return boss = ctx as WoWUnit;
                },
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(ctx => boss.IsHostile && boss.Combat, () => boss, new ScriptHelpers.AngleSpan(0, 40)),
                
                // Singular should heal friendly units if they're focused. idk about other combat routines.
                new Decorator(ctx => Me.IsHealer() && Me.FocusedUnitGuid != boss.Guid, new Action(ctx => Me.SetFocus(boss))),
                
                new Decorator(
                // move to sunbeam
                    ctx => Me.HasAura("Dread Shadows") && Me.Auras["Dread Shadows"].StackCount >= maxDreadShadowsStack && sunbeam != null,
                    new Action(ctx => Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(sunbeam.Location)))),
                
                // dispell boss
                new Decorator(
                    ctx => boss.IsFriendly && boss.HasAura("Terrorize") && (dispellSpell = ScriptHelpers.GetDefensiveMagicDispell()) != null,
                    new Action(ctx => SpellManager.Cast(dispellSpell, boss))),
                
                // healers should stand in front of boss.
                new Decorator(ctx => Me.IsHealer() && boss.IsFriendly && (!boss.IsSafelyFacing(Me, 30) || boss.Distance > 15),
                   new Action(ctx => Navigator.MoveTo(boss.Location.RayCast(WoWMathHelper.NormalizeRadian(boss.Rotation), 10))))
                    );
        }

        #endregion

        #region Lei Shi

        private const uint LeiShiId = 62983;
        private const int GetAwayId = 123461;

        private readonly string[] _aoeSpellNames = new[]
                                                   {
                                                       "Divine Storm","Thunder Clap","Heroic Leap",  "Arcane Explosion", "Rain of Fire", "Hurricane", "Fan of Knives", "Death and Decay",
                                                       "Explosive Trap", "Magma Totem"
                                                   };

        [EncounterHandler(62983, "Lei Shi")]
        public Composite LeiShiEncounter()
        {
            WoWUnit boss = null;
            // stay away from the spray target.
            AddAvoidObject(ctx => true, 3, o => o.Entry == LeiShiId && o.ToUnit().CurrentTargetGuid != 0 && o.ToUnit().CurrentTargetGuid != Me.Guid, o => o.ToUnit().CurrentTarget.Location);

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(ctx => boss.CastingSpellId == GetAwayId && boss.Distance > 25, new Action(ctx => Navigator.PlayerMover.MoveTowards(boss.Location))));
        }

        [EncounterHandler(63099, "Lei Shi Hidden", Mode = CallBehaviorMode.Proximity)]
        public Composite HiddenLeiShiEncounter()
        {
            WoWUnit hiddenLeiShi = null;
            WoWSpell aoeSpell = null;
            return new PrioritySelector(
                ctx =>
                {
                    aoeSpell = GetAoeSpell();
                    return hiddenLeiShi = ctx as WoWUnit;
                },
                new Decorator(
                    ctx => !Me.IsHealer() && aoeSpell != null,
                    new PrioritySelector(
                        new Decorator(
                            ctx => hiddenLeiShi.Distance > (aoeSpell.IsMeleeSpell || aoeSpell.MaxRange == 0 ? 4 : 30),
                            new Action(ctx => Navigator.MoveTo(hiddenLeiShi.Location))),
                        new Decorator(
                            ctx => hiddenLeiShi.Distance <= (aoeSpell.IsMeleeSpell || aoeSpell.MaxRange == 0 ? 4 : 30),
                            new PrioritySelector(
                                new Decorator(ctx => Me.IsMoving, new Action(ctx => WoWMovement.MoveStop())),
                                new Decorator(
                                    ctx => SpellManager.CanCast(aoeSpell) || Me.Class == WoWClass.Shaman && Me.Totems.All(t => t.WoWTotem != WoWTotem.Magma),
                                    new Sequence(
                                        new Action(ctx => SpellManager.Cast(aoeSpell)),
                                        new WaitContinue(1, context => false, new ActionAlwaysSucceed()),
                                        new DecoratorContinue(ctx => Me.CurrentPendingCursorSpell != null,
                                            new Action(ctx => SpellManager.ClickRemoteLocation(hiddenLeiShi.Location)))
                                        )))),
                        new ActionAlwaysSucceed())));
        }

        private WoWSpell GetAoeSpell()
        {
            SpellFindResults results = null;
            return _aoeSpellNames.Where(s => SpellManager.FindSpell(s, out results)).Select(s => results.Override ?? results.Original).FirstOrDefault();
        }


        #endregion

        #region Sha of Fear

        private const uint YangGuoshiId = 61038;
        private const uint JinlunKunId = 61046;
        private const uint ChengKangId = 61042;
        private const uint TerrorSpawnId = 61034;
        private const uint ReturnToTheTerraceId = 65736;
        private const uint ShaGlobeId = 65691;
        private readonly uint[] _terraceGuardians = new[] { YangGuoshiId, JinlunKunId, ChengKangId };

        [EncounterHandler(60999, "Sha of Fear", BossRange = 15000)]
        public Composite ShaOfFearEncounter()
        {
            WoWUnit boss = null;
            WoWUnit myTarget = null;
            WoWUnit returnToTerrace = null;

            // don't stand on top of the terror spawns because they can still deflect attacks if standing too close.
            AddAvoidObject(ctx => true, 2, TerrorSpawnId);
            return new PrioritySelector(
                ctx =>
                {
                    returnToTerrace = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == ReturnToTheTerraceId && u.Distance <= 50);
                    return boss = ctx as WoWUnit;
                },
                // stand behind the Terror Spawns..
                ScriptHelpers.GetBehindUnit(ctx => (myTarget = Me.CurrentTarget) != null && myTarget.Entry == TerrorSpawnId && myTarget.IsFacing(Me), () => myTarget),
                // interact with the 'Return to the Terrace" portal
                new Decorator(
                    ctx => returnToTerrace != null,
                    new PrioritySelector(
                        new Decorator(ctx => returnToTerrace.WithinInteractRange, new Action(ctx => returnToTerrace.Interact())),
                        new Decorator(ctx => !returnToTerrace.WithinInteractRange, new Action(ctx => Navigator.MoveTo(returnToTerrace.Location))))));
        }

        #endregion
    }
}